package com.campus.counseling.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.campus.counseling.entity.Appointment;
import com.campus.counseling.entity.Schedule;
import com.campus.counseling.model.dto.BatchScheduleDTO;
import com.campus.counseling.model.dto.ScheduleDTO;
import com.campus.counseling.model.mapper.AppointmentMapper;
import com.campus.counseling.model.mapper.ScheduleMapper;
import com.campus.counseling.model.query.ScheduleQuery;
import com.campus.counseling.model.vo.ScheduleVO;
import com.campus.counseling.model.vo.TimeSlotVO;
import com.campus.counseling.service.ScheduleService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class ScheduleServiceImpl extends ServiceImpl<ScheduleMapper, Schedule> implements ScheduleService {

    private final ScheduleMapper scheduleMapper;
    private final ObjectMapper objectMapper;
    private final AppointmentMapper appointmentMapper;
    private static final Logger log = LoggerFactory.getLogger(ScheduleServiceImpl.class);

    @Autowired
    public ScheduleServiceImpl(ScheduleMapper scheduleMapper, ObjectMapper objectMapper, AppointmentMapper appointmentMapper) {
        this.scheduleMapper = scheduleMapper;
        this.objectMapper = objectMapper;
        this.appointmentMapper = appointmentMapper;
    }

    @Override
    @SneakyThrows
    public List<ScheduleVO> getScheduleList(ScheduleQuery query) {
        List<ScheduleVO> schedules = scheduleMapper.selectScheduleList(query);
        if (schedules != null) {
            for (ScheduleVO schedule : schedules) {
                if (schedule.getTimeSlots() == null) {
                    schedule.setTimeSlots("[]");
                }
            }
        }
        return schedules != null ? schedules : new ArrayList<>();
    }

    @Override
    @Transactional
    @SneakyThrows
    public void addSchedule(ScheduleDTO dto) {
        Schedule schedule = scheduleMapper.selectOne(new LambdaQueryWrapper<Schedule>()
            .eq(Schedule::getCounselorId, dto.getCounselorId())
            .eq(Schedule::getScheduleDate, dto.getScheduleDate()));
        
        if (schedule == null) {
            schedule = new Schedule();
        }
        
        schedule.setCounselorId(dto.getCounselorId());
        schedule.setScheduleDate(dto.getScheduleDate());
        schedule.setTimeSlots(objectMapper.writeValueAsString(dto.getTimeSlots()));
        schedule.setUpdateTime(LocalDateTime.now());
        
        this.saveOrUpdate(schedule);
    }

    @Override
    @Transactional
    @SneakyThrows
    public void batchAddSchedule(BatchScheduleDTO dto) {
        LocalDate startDate = dto.getStartDate();
        LocalDate endDate;
        
        // 根据周期确定结束日期
        if ("month".equals(dto.getPeriod())) {
            endDate = startDate.plusMonths(1).minusDays(1);
        } else {
            endDate = startDate.plusYears(1).minusDays(1);
        }

        // 将时间段列表转换为JSON格式的字符串
        String timeSlots = objectMapper.writeValueAsString(dto.getTimeSlots());

        LocalDate currentDate = startDate;
        while (!currentDate.isAfter(endDate)) {
            // 检查是否是节假日
            if (isHoliday(currentDate)) {
                log.info("日期 {} 为节假日，跳过排班", currentDate);
                currentDate = currentDate.plusDays(1);
                continue;
            }

            try {
                // 先查询是否存在
                Schedule existingSchedule = scheduleMapper.selectOne(
                    new LambdaQueryWrapper<Schedule>()
                        .eq(Schedule::getCounselorId, dto.getCounselorId())
                        .eq(Schedule::getScheduleDate, currentDate)
                );

                if (existingSchedule != null) {
                    // 如果存在，合并时间段
                    List<String> existingSlots = objectMapper.readValue(existingSchedule.getTimeSlots(), new TypeReference<List<String>>() {});
                    Set<String> mergedSlots = new HashSet<>(existingSlots);
                    mergedSlots.addAll(dto.getTimeSlots());
                    
                    // 转换回JSON格式
                    existingSchedule.setTimeSlots(objectMapper.writeValueAsString(new ArrayList<>(mergedSlots)));
                    scheduleMapper.updateById(existingSchedule);
                    
                    log.info("更新排班成功: id={}, date={}, slots={}", 
                            existingSchedule.getId(), currentDate, mergedSlots);
                } else {
                    // 如果不存在，创建新记录
                    Schedule schedule = new Schedule();
                    schedule.setCounselorId(dto.getCounselorId());
                    schedule.setScheduleDate(currentDate);
                    schedule.setTimeSlots(timeSlots);  // 使用JSON格式的时间段
                    scheduleMapper.insert(schedule);
                    
                    log.info("创建排班成功: counselorId={}, date={}, slots={}", 
                            dto.getCounselorId(), currentDate, dto.getTimeSlots());
                }
            } catch (Exception e) {
                log.error("处理排班失败: counselorId={}, date={}, error={}", 
                        dto.getCounselorId(), currentDate, e.getMessage());
            }
            
            currentDate = currentDate.plusDays(1);
        }
    }

    @Override
    @Transactional
    @SneakyThrows
    public void updateSchedule(ScheduleDTO scheduleDTO) {
        Schedule schedule = new Schedule();
        BeanUtils.copyProperties(scheduleDTO, schedule);
        schedule.setScheduleDate(scheduleDTO.getScheduleDate());
        
        // 确保timeSlots不为null
        List<String> timeSlots = scheduleDTO.getTimeSlots();
        if (timeSlots == null || timeSlots.isEmpty()) {
            timeSlots = new ArrayList<>();
        }
        
        schedule.setTimeSlots(objectMapper.writeValueAsString(timeSlots));
        scheduleMapper.updateById(schedule);
    }

    @Override
    @Transactional
    public void deleteSchedule(Long id) {
        scheduleMapper.deleteById(id);
    }

    @Override
    public List<TimeSlotVO> getAvailableTimeSlots(Long counselorId, LocalDate date) {
        // 定义标准时间段
        final List<String> standardTimeSlots = Arrays.asList(
            "09:00-10:00",
            "10:00-11:00",
            "14:00-15:00",
            "15:00-16:00",
            "16:00-17:00",
            "17:00-18:00"
        );
        
        // 1. 获取该咨询师在这一天的排班记录
        Schedule schedule = scheduleMapper.selectOne(new LambdaQueryWrapper<Schedule>()
            .eq(Schedule::getCounselorId, counselorId)
            .eq(Schedule::getScheduleDate, date));
        
        // 2. 获取已排班的时间段
        final List<String> scheduledTimeSlots = new ArrayList<>();
        if (schedule != null && schedule.getTimeSlots() != null) {
            try {
                List<String> slots = objectMapper.readValue(schedule.getTimeSlots(), new TypeReference<List<String>>() {});
                scheduledTimeSlots.addAll(slots);  // 使用 addAll 而不是直接赋值
            } catch (Exception e) {
                log.error("解析时间段失败: {}", e.getMessage());
            }
        }
        
        // 3. 获取该日期的所有预约记录
        LocalDateTime startTime = date.atStartOfDay();
        LocalDateTime endTime = date.atTime(23, 59, 59);
        List<Appointment> appointments = appointmentMapper.selectList(new LambdaQueryWrapper<Appointment>()
            .eq(Appointment::getCounselorId, counselorId)
            .eq(Appointment::getDeleted, false)
            .between(Appointment::getAppointmentTime, startTime, endTime));
        
        // 4. 将预约记录转换为Map
        final Map<String, Appointment> appointmentMap = appointments.stream()
            .collect(Collectors.toMap(
                apt -> apt.getAppointmentTime().format(DateTimeFormatter.ofPattern("HH:mm")),
                apt -> apt,
                (existing, replacement) -> existing
            ));
        
        // 5. 构建所有时间段的状态
        return standardTimeSlots.stream().map(timeSlot -> {
            String[] times = timeSlot.split("-");
            String endTimeStr = times[1];  // 使用结束时间
            
            TimeSlotVO vo = new TimeSlotVO();
            vo.setTimeSlot(timeSlot);
            
            // 检查是否已过期 - 使用结束时间判断
            LocalDateTime slotEndDateTime = date.atTime(
                Integer.parseInt(endTimeStr.split(":")[0]),
                Integer.parseInt(endTimeStr.split(":")[1])
            );
            
            // 只有当前时间超过了结束时间，才认为过期
            if (LocalDateTime.now().isAfter(slotEndDateTime)) {
                vo.setStatus("EXPIRED");
                vo.setDisplayText("已过期");
                return vo;
            }
            
            // 检查是否已排班
            if (!scheduledTimeSlots.contains(timeSlot)) {
                vo.setStatus("UNSCHEDULED");
                vo.setDisplayText("未排班");
                return vo;
            }
            
            // 检查是否已预约
            Appointment appointment = appointmentMap.get(times[0]);
            if (appointment != null) {
                vo.setStatus(appointment.getStatus());
                vo.setAppointmentId(appointment.getId());
                vo.setStudentName(appointment.getStudentName());
                vo.setDisplayText(getDisplayText(appointment));
            } else {
                vo.setStatus("AVAILABLE");
                vo.setDisplayText("可预约");
            }
            
            return vo;
        }).collect(Collectors.toList());
    }

    private String getDisplayText(Appointment appointment) {
        if (appointment == null) return "可预约";
        
        String studentName = appointment.getStudentName() != null ? 
            " - " + appointment.getStudentName() : "";
            
        switch (appointment.getStatus()) {
            case "CONFIRMED":
                return "已预约" + studentName;
            case "PENDING":
                return "待审核" + studentName;
            case "APPROVED":
                return "已通过" + studentName;
            case "REJECTED":
                return "已拒绝" + studentName;
            case "COMPLETED":
                return "已完成" + studentName;
            case "CANCELLED":
                return "已取消" + studentName;
            default:
                return "可预约";
        }
    }

    /**
     * 判断指定日期是否为节假日
     * 1. 判断是否为周末
     * 2. 判断是否为法定节假日
     */
    private boolean isHoliday(LocalDate date) {
        // 1. 判断是否为周末（周六和周日）
        DayOfWeek dayOfWeek = date.getDayOfWeek();
        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
            return true;
        }
    
        // 2. 判断是否为法定节假日
        int year = date.getYear();
        int month = date.getMonthValue();
        int day = date.getDayOfMonth();
    
        // 元旦（1 月 1 日）
        if (month == 1 && day == 1) {
            return true;
        }
    
        // 春节（2025 年 1 月 28 日至 2 月 4 日）
        if (year == 2025 && ((month == 1 && day >= 28) || (month == 2 && day <= 4))) {
            return true;
        }
    
        // 清明节（4 月 4 日至 6 日）
        if (month == 4 && day >= 4 && day <= 6) {
            return true;
        }
    
        // 劳动节（5 月 1 日至 5 日）
        if (month == 5 && day >= 1 && day <= 5) {
            return true;
        }
    
        // 端午节（5 月 31 日至 6 月 2 日）
        if (year == 2025 && month == 5 && day >= 31 || (month == 6 && day <= 2)) {
            return true;
        }
    
        // 中秋节（9 月 17 日）和国庆节（10 月 1 日至 8 日）
        if ((month == 9 && day == 17) || (month == 10 && day >= 1 && day <= 8)) {
            return true;
        }
    
        return false;
    }
} 